#pragma once

#include <iostream>
#include <queue>
#include <unistd.h>
#include "localmutex.hpp"
#include "localcond.hpp"

namespace Local_Prod_Cons_Mod
{
    template <typename T>
    class LocalBlockQueue
    {
    private:
        bool IsFull()
        {
            return _blockqueue.size() == _capacity;
        }
        bool IsEmpty()
        {
            return _blockqueue.empty();
        }

    public:
        // 构造函数
        LocalBlockQueue(int capacity)
            : _capacity(capacity), _prodwaitnum(0), _conswaitnum(0)
        {
        }
        // 析构函数
        ~LocalBlockQueue()
        {
        }
        // 拷贝构造
        LocalBlockQueue(const LocalBlockQueue<T> &cpy) = delete;
        // 赋值重载
        LocalBlockQueue operator=(const LocalBlockQueue<T> &cpy) = delete;

        // 生产
        void Push(T *pt)
        {
            {
                // 1.竞争锁
                Local_Mutex::LockGuard lockguard(&_mutex);
                // 2.循环判断队列是否放满
                while (IsFull())
                {
                    // 等待挂起
                    _prodwaitnum++;
                    _prodcond.Wait(&(_mutex._mutex));
                    _prodwaitnum--;
                }
                // 3.入队列
                _blockqueue.push(*pt);
                // 4.唤醒消费者
                if (_conswaitnum > 0)
                {
                    _conscond.Signal();
                }
                // 5.释放锁
            }
        }
        // 消费
        T Pop()
        {
            // 此处要求传自定义类型的时候需要提供默认构造
            T t;
            {
                // 1.竞争锁
                Local_Mutex::LockGuard lockguard(&_mutex);
                // 2.循环判断队列是否为空
                while (IsEmpty())
                {
                    // 等待挂起
                    _conswaitnum++;
                    _conscond.Wait(&(_mutex._mutex));
                    _conswaitnum--;
                }
                // 3.出队列
                // 此处要求传自定义类型的时候需要提供赋值重载
                t = _blockqueue.front();
                _blockqueue.pop();
                // 4.唤醒生产者
                if (_prodwaitnum > 0)
                {
                    _prodcond.Signal();
                }
                // 5.释放锁
            }
            return t;
        }

    private:
        std::queue<T> _blockqueue; // 阻塞队列
        int _capacity;             // 容量

        Local_Mutex::LocalMutex _mutex;  // 互斥量
        Local_Cond::LocalCond _prodcond; // 生产者条件变量
        Local_Cond::LocalCond _conscond; // 消费者条件变量

        int _prodwaitnum; // 生产者等待数量
        int _conswaitnum; // 消费者等待数量
    };
}